﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using SharpObjects.Multithreading;
using SharpObjects.MultiThreading;

namespace SharpObjects.Multithreading_Test
{
    class SharpObjects_Multithreading_Test_Program
    {
        static void Main(string[] args)
        {
            TestUsageWithInputJobQueue();
            TestUsageWithGetJobEvent();
        }

        private static List<IJob> jobs = null;

        /// <summary>
        /// This method creates a collection of job objects and adds them to the 
        /// thread pool job queue. Threads will get the jobs from the queue and process them
        /// </summary>
        static void TestUsageWithInputJobQueue()
        {
            int threadCount = 4;

            using (ThreadPool<IJob, ThreadContext> threadPool = new ThreadPool<IJob, ThreadContext>(threadCount, TimeSpan.FromMilliseconds(100)))
            {
                Console.WriteLine("Getting jobs from the thread pool queue.\nThe combination of a context and a thread ID will stay unchanged\n");

                threadPool.InitializeThreadContext += new ThreadPool<IJob, ThreadContext>.InitThreadContextHandler(threadPool_InitializeThreadContext);
                threadPool.ProcessThreadJob += new ThreadPool<IJob, ThreadContext>.ProcessJobHandler(threadPool_ProcessThreadJob);
                threadPool.StartThreads();

                jobs = new List<IJob>()
                                  {
                                      new Job(){ Delay = TimeSpan.FromSeconds(1), Message = "Job 1"},
                                      new Job(){ Delay = TimeSpan.FromSeconds(2), Message = "Job 2"},
                                      new Job(){ Delay = TimeSpan.FromSeconds(3), Message = "Job 3"},
                                      new Job(){ Delay = TimeSpan.FromSeconds(1), Message = "Job 4"},
                                      new Job(){ Delay = TimeSpan.FromSeconds(2), Message = "Job 5"},
                                      new Job(){ Delay = TimeSpan.FromSeconds(3), Message = "Job 6"},
                                      new Job(){ Delay = TimeSpan.FromSeconds(1), Message = "Job 7"},
                                      new Job(){ Delay = TimeSpan.FromSeconds(2), Message = "Job 8"}
                                  };

                foreach (IJob job in jobs)
                {
                    threadPool.AddJob(job);
                }

                jobs.Clear();

                //let at least one thread pick up a job
                Thread.Sleep(1);

                // wait until all threads are done
                while (true)
                {
                    if(threadPool.IsPoolIdle)
                    {
                        Thread.Sleep(TimeSpan.FromMilliseconds(500));

                        if(threadPool.IsPoolIdle)
                        {
                            break;
                        }
                    }

                    Thread.Sleep(TimeSpan.FromSeconds(1));
                }


                Console.WriteLine("\nStopping threads\n");
                threadPool.StopThreads();
            }
        }


        /// <summary>
        /// This method uses the GetJob event to pull jobs out of job collection
        /// </summary>
        static void TestUsageWithGetJobEvent()
        {
            int threadCount = 4;

            using (ThreadPool<IJob, ThreadContext> threadPool = new ThreadPool<IJob, ThreadContext>(threadCount, TimeSpan.FromMilliseconds(100)))
            {
                Console.WriteLine("\n_______________________\nGetting jobs using GetJob event.\nThe combination of a context and a thread ID will stay unchanged\n");

                threadPool.InitializeThreadContext += new ThreadPool<IJob, ThreadContext>.InitThreadContextHandler(threadPool_InitializeThreadContext);
                threadPool.ProcessThreadJob += new ThreadPool<IJob, ThreadContext>.ProcessJobHandler(threadPool_ProcessThreadJob);
                threadPool.GetJob += new ThreadPool<IJob, ThreadContext>.GetJobHandler(threadPool_GetJob);
                threadPool.StartThreads();

                jobs = new List<IJob>()
                                  {
                                      new Job(){ Delay = TimeSpan.FromSeconds(1), Message = "Job 1"},
                                      new Job(){ Delay = TimeSpan.FromSeconds(2), Message = "Job 2"},
                                      new Job(){ Delay = TimeSpan.FromSeconds(3), Message = "Job 3"},
                                      new Job(){ Delay = TimeSpan.FromSeconds(1), Message = "Job 4"},
                                      new Job(){ Delay = TimeSpan.FromSeconds(2), Message = "Job 5"},
                                      new Job(){ Delay = TimeSpan.FromSeconds(3), Message = "Job 6"},
                                      new Job(){ Delay = TimeSpan.FromSeconds(1), Message = "Job 7"},
                                      new Job(){ Delay = TimeSpan.FromSeconds(2), Message = "Job 8"}
                                  };



                //let at least one thread pick up a job
                Thread.Sleep(1);

                // wait until all threads are done
                while (true)
                {
                    if (threadPool.IsPoolIdle)
                    {
                        Thread.Sleep(TimeSpan.FromMilliseconds(500));

                        if (threadPool.IsPoolIdle)
                        {
                            break;
                        }
                    }

                    Thread.Sleep(TimeSpan.FromSeconds(1));
                }

                Console.WriteLine("\nStopping threads\n");
                threadPool.StopThreads();
            }
        }

        static IJob threadPool_GetJob()
        {
            lock(jobs)
            {
                if(jobs.Count > 0)
                {
                    IJob job = jobs[0];
                    jobs.RemoveAt(0);

                    return job;
                }
                else
                {
                    return null;
                }
            }
        }

        /// <summary>
        /// Processes each job a thread gets
        /// </summary>
        /// <param name="job"></param>
        /// <param name="context"></param>
        static void threadPool_ProcessThreadJob(IJob job, ThreadContext context)
        {
            job.Execute(context);
        }

        static int contextCount = 0;

        /// <summary>
        /// Creates and initializes thread context for each thread - each thread gets its own context
        /// The combination of a context and a thread ID will stay unchanged (see output)
        /// </summary>
        /// <returns></returns>
        static ThreadContext threadPool_InitializeThreadContext()
        {
            ThreadContext threadContext = new ThreadContext();
            threadContext.Label = string.Format("Thread Context {0}", contextCount++);
            return threadContext;
        }
    }
}
